package logic;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;

public class MushroomNB extends MushroomClassifier {
    public ArrayList<HashMap<Character, HashMap<Character, Integer>>> counts = new ArrayList<HashMap<Character, HashMap<Character, Integer>>>();
    
    public MushroomNB() {
        
    }
    
    public final void initCounts() {
        for (int i = 0; i < usedAttrs.length; i++) {
            HashMap<Character, HashMap<Character, Integer>> attrCounts = new HashMap<Character, HashMap<Character, Integer>>();
            for (char attr : MushroomInfo.ATTRIBUTE_VALUES_SHORT[i]) {
                HashMap<Character, Integer> valCounts = new HashMap<Character, Integer>();
                for (char c : MushroomInfo.CLASS_VALUES_SHORT) {
                    valCounts.put(c, 0);
                }
                attrCounts.put(attr, valCounts);
            }
            counts.add(attrCounts);
        }
    }
    
    public void addCount(int attrIdx, char attrValue, char classValue) {
        if (usedAttrs[attrIdx] == false)
            return;
        
        HashMap<Character, Integer> count = counts.get(attrIdx).get(attrValue);
        int inc = count.get(classValue) + 1;
        count.put(classValue, inc);
    }
    
    @Override
    public void buildClassifier(Instances is) throws Exception {
        initUsedAttrs(is.firstInstance());
        initCounts();
        initClassValues(is.classAttribute());
        
        Enumeration isEnu = is.enumerateInstances();
        while (isEnu.hasMoreElements()) {
            Instance in = (Instance)isEnu.nextElement();
            String[] s = in.toString().split(",");
            
            char cls = s[s.length-1].charAt(0);
            for (int j = 0; j < MushroomInfo.NUM_ATTRIBUTES; j++) {
                addCount(j, s[j].charAt(0), cls);
            }
        }
    }
    
    @Override
    public char classify(Instance i) {
        boolean[] selectionAttrs = getUsedAttrs(i);
        selectionAttrs[MushroomInfo.CLASS_IDX] = false;
        return classify(i, selectionAttrs);
    }
    @Override
    public char classify(Instance i, boolean[] selectionAttrs) {
        HashMap<Character, Double> clsProb = new HashMap<Character, Double>();
        int numInst = getNumInstance();
        for (char cls : MushroomInfo.CLASS_VALUES_SHORT) {
            int num = counts.get(MushroomInfo.CLASS_IDX).get(cls).get(cls);
            double d = ((double)num)/numInst;
            
            String[] splits = i.toString().split(",");
            int splitIdx = 0;
            for (int j = 0; j < MushroomInfo.CLASS_IDX; j++) {
                if (usedAttrs[j] == false || selectionAttrs[j] == false)
                    continue;
                
                char value = splits[splitIdx++].charAt(0);
                int numAttr = counts.get(j).get(value).get(cls);
                d = (d*numAttr)/num;
            }
            
            clsProb.put(cls, d);
        }
        
        double max = 0;
        char classifiedAs = '\0';
        for (char cls : clsProb.keySet()) {
            if (clsProb.get(cls) > max) {
                max = clsProb.get(cls);
                classifiedAs = cls;
            }
        }
        
        return classifiedAs;
    }
    
    public void learn(Instance i, char label) {
        boolean[] selectionAttrs = getUsedAttrs(i);
        learn(i, selectionAttrs, label);
    }
    public void learn(Instance i, boolean[] selectionAttrs, char label) {
        String[] s = i.toString().split(",");
        int splitIdx = 0;
        for (int j = 0; j < MushroomInfo.CLASS_IDX; j++) {
            if ((selectionAttrs[j] == true) && (usedAttrs[j] == true)) {
                addCount(j, s[splitIdx++].charAt(0), label);
            }
        }
        addCount(MushroomInfo.CLASS_IDX, label, label);
    }
    
    public int getNumInstance() {
        int numInst = 0;
        
        for (char cls : MushroomInfo.CLASS_VALUES_SHORT) {
            numInst += counts.get(MushroomInfo.CLASS_IDX).get(cls).get(cls);
        }
        
        return numInst;
    }
    
    public String globalInfo() {
        return "Naive Bayes untuk Mushroom Dataset";
    }
    
    public String stringModel() {
        ArrayList<HashMap<Character, String>> longs = MushroomInfo.singleton.ATTRIBUTE_VALUES_LONG;
        StringBuilder s = new StringBuilder();
        
        int i = 0;
        for (HashMap<Character, HashMap<Character, Integer>> attrCounts : counts) {
            if (attrCounts == null)
                continue;
            
            s.append(MushroomInfo.ATTRIBUTE_NAMES[i]).append("\n");
            for (char attr : attrCounts.keySet()) {
                s.append(longs.get(i).get(attr)).append("    ").append(attrCounts.get(attr).toString()).append("\n");
            }
            s.append("\n");
            i++;
        }
        
        return s.toString();
    }
    
    public static void main(String[] args) {
        Instances inst = Loader.load("/Users/danielginting/Desktop/mushroom.arff");
        inst = Loader.preProcess(inst, Loader.UNSUPERVISED_DISCRETIZE);
        MushroomNB nb = new MushroomNB(); //(MushroomNB)Loader.loadModel(Loader.MODEL_MUSHROOM_NB, "D:\\mushroom.model");
        
        try {
            nb.buildClassifier(inst);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println(nb.stringModel());
        Enumeration e = inst.enumerateInstances();
        int i = 1;
        HashMap<Character, String> clsNames = MushroomInfo.singleton.ATTRIBUTE_VALUES_LONG.get(MushroomInfo.CLASS_IDX);
        while (e.hasMoreElements()) {
            Instance is = (Instance)e.nextElement();
            System.out.println((i++) + ": " + nb.classify(is));
        }
        Instance ins = inst.instance(2);
        
        char x = nb.classify(ins);
        System.out.println("Jawab : " + x);
        System.out.println(nb.accuracy(inst));
//        Loader.saveModel(nb, Loader.MODEL_MUSHROOM_NB, "D:\\mushroom.model");
    }
}
